package com.example.demo.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author: yby
 * @desc:
 * @date: 2024/10/23 18:31
 */
@Slf4j
public class Auth {
   public static final String UTF_8 = "UTF-8";
   public final String accessKey;
   private final SecretKeySpec secretKey;
   private List<Long> accessKeyTenant;
   public Auth(String accessKey, SecretKeySpec secretKeySpec) {
      this.accessKey = accessKey;
      this.secretKey = secretKeySpec;
   }
   private Auth(String accessKey, SecretKeySpec secretKeySpec, List<Long> tenantIds) {
      this.accessKey = accessKey;
      this.secretKey = secretKeySpec;
      this.accessKeyTenant = tenantIds;
   }
   public static Auth create(String accessKey, String secretKey) throws UnsupportedEncodingException {
      if (StringUtils.isEmpty(accessKey) || StringUtils.isEmpty(secretKey)) {
         throw new IllegalArgumentException("empty key");
      }
      byte[] sk = utf8Bytes(secretKey);
      SecretKeySpec secretKeySpec = new SecretKeySpec(sk, "HmacSHA1");
      return new Auth(accessKey, secretKeySpec);
   }
   public static Auth create(String accessKey, String secretKey, List<Long> tenantIds) throws UnsupportedEncodingException {
      if (StringUtils.isEmpty(accessKey) || StringUtils.isEmpty(secretKey)) {
         throw new IllegalArgumentException("empty key");
      }
      byte[] sk = utf8Bytes(secretKey);
      SecretKeySpec secretKeySpec = new SecretKeySpec(sk, "HmacSHA1");
      return new Auth(accessKey, secretKeySpec, tenantIds);
   }
   public String authorization(String method, String url, String contentType, Long timeStack) throws UnsupportedEncodingException {
      return signRequestV2(url, method, contentType, timeStack);
   }
   public String authorization(String method, Map<String, String> headers, String path, Long timeStack) {
      try {
         return signRequestV2(path, method, headers, null, timeStack);
      } catch (Exception e) {
         log.debug("authorization error", e);
         return null;
      }
   }
   public String authorizationTenant(String method, Map<String, String> headers, String path, Long timeStack, Long tenantId) {
      try {
//检验accessKey是否有租户权限
         if (accessKeyTenant.contains(tenantId)) {
            return signRequestV2(path, method, headers, null, timeStack, tenantId);
         } else {
            return "";
         }
      } catch (Exception e) {
         log.debug("authorization error", e);
         return null;
      }
   }
   public String authorization(String method, String path, Long timeStack) {
      try {
         return authorization(method, path, null, timeStack);
      } catch (Exception e) {
         log.debug("authorization error", e);
         return null;
      }
   }
   public String sign(byte[] data,Long timeStack) {
      Mac mac = createMac();
      String encodedSign = cn.hutool.core.codec.Base64.encodeUrlSafe(mac.doFinal(data));
      return String.format("%s:%s:%s", this.accessKey, timeStack, encodedSign);
   }
   public String sign(String signString) {
      Mac mac = createMac();
      return cn.hutool.core.codec.Base64.encodeUrlSafe(mac.doFinal(signString.getBytes(StandardCharsets.UTF_8)));
   }
   public String sign(String data,Long timeStack) {
      return sign(data.getBytes(StandardCharsets.UTF_8),timeStack);
   }
   public static byte[] utf8Bytes(String data) throws UnsupportedEncodingException {
      return data.getBytes(UTF_8);
   }
   public String signRequestV2(String urlString, String method, String contentType, Long timeStack) throws UnsupportedEncodingException {
      return String.format("%s:%s:%s", this.accessKey, timeStack, digest(urlString, method, contentType, timeStack));
   }
   public String signRequestV2(String urlString, String method, Map<String, String> headers, String contentType, Long timeStack) throws UnsupportedEncodingException {
      return String.format("%s:%s:%s", this.accessKey, timeStack, digest(urlString, method, headers, contentType, timeStack));
   }
   public String signRequestV2(String urlString, String method, Map<String, String> headers, String contentType, Long timeStack, Long tenantId) throws UnsupportedEncodingException {
      return String.format("%s:%s:%s:%s", this.accessKey, tenantId, timeStack, digest(urlString, method, headers, contentType, timeStack));
   }
   public String digest(String path, String method, String contentType, Long timeStack) throws UnsupportedEncodingException {
      Mac mac = createMac();
      StringBuilder sb = new StringBuilder();
      sb.append(String.format("%s %s", method, path));
      if (timeStack != null) {
         sb.append(String.format("\ntimeStack: %s", timeStack));
      }
      if (contentType != null) {
         sb.append(String.format("\nContent-Type: %s", contentType));
      }
      mac.update(utf8Bytes(sb.toString()));
      String digest = Base64.encodeBase64URLSafeString(mac.doFinal());
      return digest;
   }
   public String digest(String path, String method, Map<String, String> headers, String contentType, Long timeStack) throws UnsupportedEncodingException {
      Mac mac = createMac();
      StringBuilder sb = new StringBuilder();
      if (!CollectionUtils.isEmpty(headers)) {
//按header Key ASCII 正序
         List<String> keys = new ArrayList<>(headers.keySet());
         Collections.sort(keys);
         keys.forEach(x -> sb.append(String.format("%s:%s\n", x, headers.get(x))));
      }
      sb.append(String.format("%s %s", method, path));
      if (timeStack != null) {
         sb.append(String.format("\ntimeStack: %s", timeStack));
      }
      if (contentType != null) {
         sb.append(String.format("\nContent-Type: %s", contentType));
      }
      mac.update(utf8Bytes(sb.toString()));
      String digest = Base64.encodeBase64URLSafeString(mac.doFinal());
      return digest;
   }
   private Mac createMac() {
      Mac mac;
      try {
         mac = Mac.getInstance("HmacSHA1");
         mac.init(secretKey);
      } catch (GeneralSecurityException e) {
         log.error("create mac erorr", e);
         throw new IllegalArgumentException(e);
      }
      return mac;
   }
   public static void main(String[] args) throws UnsupportedEncodingException {
      String accessKey = "111";
      String secretKey = "222";
      Auth auth = Auth.create(accessKey, secretKey);
      String token = auth.authorization("GET", "http/authcenter/openapi/v3/getUserInfoByAccessToken?accessToken=cc0bb68855e7c75fe3484973ce900860", System.currentTimeMillis());
      System.out.println(token);
   }
}
